package com.sh.caryr.erp.util;

import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.plugins.Page;
import com.baomidou.mybatisplus.service.IService;
import com.sh.caryr.erp.custmz.UserAuthorityContainer;
import com.sh.caryr.erp.entity.BaseEntity;
import com.sh.caryr.erp.entity.PageResultMessage;
import com.sh.caryr.erp.entity.trade.AsOrder;
import com.sh.caryr.erp.entity.trade.TradeOrder;
import com.sh.caryr.erp.entity.user.User;
import com.sh.caryr.erp.entity.user.UserRole;
import com.sh.caryr.erp.exception.RestServiceException;
import com.sh.caryr.erp.helper.mybatis.AliasWrapper;
import net.sf.json.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpStatus;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Iterator;
import java.util.List;

public class MyBatisUtil {

    public static final int SELECT_ALL = -1;
    public static final int PAGE_SIZE_MAX = 1000;

    public static Wrapper getActiveQueryWrapper(String queryField, String queryValue) {
        Wrapper wrapper = getIsActiveWrapper(queryField, queryValue, null, false);
        wrapper.eq("isActive", "1");
        return wrapper;
    }

    public static int offset(int current, int size) {
        if (current > 0) {
            return (current - 1) * size;
        }
        return 0;
    }

    public static Wrapper getIsActiveWrapper() {
        return getIsActiveWrapper(null, null, null);
    }

    public static Wrapper getIsActiveWrapper(String orderBy, boolean isAsc) {
        return getIsActiveWrapper(orderBy, isAsc, null);
    }

    public static Wrapper getIsActiveWrapper(String orderBy, Boolean isAsc, String tabAlias) {
        Wrapper wrapper = StringUtils.isEmpty(tabAlias) ? new AliasWrapper<>() : new AliasWrapper<>(tabAlias);
        if (orderBy != null && !"".equals(orderBy)) {
            wrapper.orderBy(orderBy, isAsc);
        }
        wrapper.eq("isActive", "1");
        return wrapper;
    }

    @SuppressWarnings("unchecked")
    public static <T> Wrapper<T> getIsActiveWrapper(String queryField, String queryValue, String orderBy, boolean isAsc) {
        Wrapper<T> wrapper = getIsActiveWrapper(orderBy, isAsc);
        appendQuery2Wrapper(wrapper, StringUtil.split(queryField, StringUtil.SEPARATOR_COMMA), StringUtil.split(queryValue, StringUtil.SEPARATOR_COMMA));
        return wrapper;
    }

    public static Wrapper addAuthFilter(Wrapper wrapper, User operator, String target, boolean shopFilter, boolean createByFilter) {
        if (!ERPConstants.SYS_SUPER_USERNAME.equals(operator.getName())) {
            //Add Company Code filter
            wrapper.eq("coco", operator.getCoco());
            //IF not company user
            if (operator.getIsSuperuser() != ERPConstants.ENITYT_ACTIVE) {
                UserRole role = operator.getUserRole();
                if (role == null || role.getId() == 0 || role.getAuthList() == null || role.getAuthList().size() == 0) {
                    //IF invalid role , return nothing;
                    wrapper.eq("id", -1);
                }
                String authStr = UserAuthorityContainer.convertUserAuthToString(operator);
                //Add Shop filter
                if (shopFilter && role != null) {
                    if ((authStr.contains(target + "_read_shop")) && !(authStr.contains(target + "_read_othershop"))) {
                        wrapper.eq("shopId", role.getShopId());
                    }
                }
                //Add CreateBy filter
                if (createByFilter) {
                    if ((authStr.contains(target + "_read_selfcreate")) && !(authStr.contains(target + "_read_othercreate"))) {
                        wrapper.eq("createBy", operator.getId());
                    }
                }
            }
        }
        return wrapper;
    }

    public static void updateCommonFields(BaseEntity entity, User operator) {
        if (entity.getCoco() == null) {
            entity.setCoco(operator.getCoco());
        }

        if (entity.getCreateBy() == null) {
            entity.setCreateBy(operator.getId());
            entity.setCreateAt(Calendar.getInstance().getTime());
        } else {
            entity.setUpdateBy(operator.getId());
            entity.setUpdateAt(Calendar.getInstance().getTime());
        }
    }

    @SuppressWarnings("unchecked")
    public static PageResultMessage searchEntities(Integer current, Integer size, String filters, String orderBy, Boolean isAsc, User operator, IService service) {
        size = MyBatisUtil.SELECT_ALL == size || MyBatisUtil.PAGE_SIZE_MAX < size ? MyBatisUtil.PAGE_SIZE_MAX : size;
        Wrapper wrapper = MyBatisUtil.getIsActiveWrapper(orderBy, isAsc);
        try {
            MyBatisUtil.parseFilters(wrapper, filters);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RestServiceException(HttpStatus.BAD_REQUEST);
        }
        MyBatisUtil.addAuthFilter(wrapper, operator, ERPConstants.AUTH_TARGET_TRADE, false, true);
        Page page = service.selectPage(new Page<AsOrder>(current, size), wrapper);
        List records = page.getRecords();
        return new PageResultMessage<>(records, service.selectCount(wrapper));
    }

    public static <T> List<T> getDeleteEntities(List<T> oldEntities, List<T> newEntities, Class clz) throws Exception {
        List deleteEntities = new ArrayList<>();
        for (Object oldObj : oldEntities) {
            if (!findEntity(oldObj, newEntities, clz.getDeclaredMethod("getId"))) {
                deleteEntities.add(oldObj);
            }
        }
        return deleteEntities;
    }

    public static List<Long> getDeleteEntityIds(Class clz, List oldEntities, List newEntities) throws Exception {
        Method getIdMethod = clz.getDeclaredMethod("getId");
        List<Long> deleteIds = new ArrayList<>();
        for (Object oldObj : oldEntities) {
            if (!findEntity(oldObj, newEntities, getIdMethod)) {
                deleteIds.add((Long) getIdMethod.invoke(oldObj));
            }
        }
        return deleteIds;
    }

    public static List getEntityIds(List entities) throws Exception {
        String methodName = "getId";
        List<Long> deleteIds = new ArrayList<>();
        for (Object oldObj : entities) {
            deleteIds.add((Long) oldObj.getClass().getDeclaredMethod(methodName).invoke(oldObj));
        }
        return deleteIds;
    }

    public static Object findEntityObj(Object newObj, List entities) throws Exception {
        return findEntityObj(newObj, entities, newObj.getClass().getDeclaredMethod("getId"));
    }

    public static boolean findEntity(Object newObj, List entities, Method compareMethod) throws Exception {
        return findEntityObj(newObj, entities, compareMethod) == null ? false : true;
    }

    private static Object findEntityObj(Object newObj, List entities, Method compareMethod) throws Exception {
        Object findObj = null;
        for (Object entity : entities) {
            Long newId = (Long) compareMethod.invoke(newObj);
            Long oldId = (Long) compareMethod.invoke(entity);
            if (oldId.equals(newId)) {
                return entity;
            }
        }
        return findObj;
    }

    private static void parseFilters(Wrapper wrapper, String filterStr) {
        if (StringUtils.isEmpty(filterStr)) {
            return;
        }
        //Fix encoding bug: http://www.cnblogs.com/SaraMoring/p/5594980.html
        filterStr = new String(filterStr.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
        JSONObject jsonObject = JSONObject.fromObject(filterStr);
        Iterator it = jsonObject.keys();
        while (it.hasNext()) {
            String key = (String) it.next();
            JSONObject valObj = jsonObject.getJSONObject(key);
            String operator = valObj.getString("operator");
            switch (operator) {
                case "like":
                    wrapper.like(key, valObj.getString("value"));
                    break;
                case "between":
                    wrapper.between(key, valObj.getString("from"), valObj.getString("to"));
                    break;
                case "in":
                    wrapper.in(key, valObj.getString("value"));
                    break;
                default:
                    wrapper.eq(key, valObj.getString("value"));
            }
        }
    }

    private static void appendQuery2Wrapper(Wrapper wrapper, String[] queryFields, String[] queryValues) {
        for (int i = 0; i < queryFields.length; i++) {
            wrapper = wrapper.eq(queryFields[i], queryValues[i]);
        }
    }
}
